package cn.lyf.currey;

import java.util.*;
import java.util.function.*;
import java.lang.reflect.*;
import static cn.lyf.currey.Utils.*;

//apply方法用来做工厂
//unapply方法 模式匹配

public class Match {

    public static <T> Pattern<T> isIn(Set<T> set) {
        return a -> set.contains(a) ? Optional.ofNullable(a) : Optional.empty();
    }

    public static Pattern<?> isIn(final Object... vals) {
        return a -> {
            if (a == null)
                return Optional.empty();
            for (int i = 0; i < vals.length; i++) {
                if (enumMatch(vals[i], a))
                    return Optional.ofNullable(vals[i]);
            }
            return Optional.empty();
        };
    }

    public static <T> Pattern<T> eq(Object val) {
        return a -> isEqual(a, val) ? Optional.of(a) : Optional.empty();
    }

    @SafeVarargs
    final public static <T> Pattern<T> and(Predicate<T>... pre) {
        return a -> {
            boolean r = true;
            for (Predicate<T> p : pre)
                r = r && p.test(a);
            return r ? Optional.ofNullable(a) : Optional.empty();
        };
    }

    @SafeVarargs
    final public static <T> Pattern<T> or(Predicate<T>... pre) {
        return a -> {
            boolean r = false;
            for (Predicate<T> p : pre)
                r = r || p.test(a);
            return r ? Optional.ofNullable(a) : Optional.empty();
        };
    }

    public static Pattern<?> arryMatch(Object... vals) {
        final Optional<?> Nil = Optional.empty();
        return a -> {
            if (a == null)
                return Nil;
            if (isArray(a)) {
                if (Array.getLength(a) < vals.length)
                    return Nil;
                for (int i = 0; i < vals.length; i++) {
                    if (!enumMatch(vals[i], Array.get(a, i)))
                        return Nil;
                }
                return Optional.of(a);
            }
            if (isList(a)) {
                List<?> list = (List<?>) a;
                if (list.size() < vals.length)
                    return Nil;
                for (int i = 0; i < vals.length; i++) {
                    if (!enumMatch(vals[i], list.get(i)))
                        return Nil;
                }
                return Optional.of(a);
            }
            return Optional.empty();
        };
    }

    public static <T> Pattern<T> analysis(final Object... args) {
        return a -> unapply(a, args);
    }

    public static Pattern<String> regex(String regex) {
        java.util.regex.Pattern p = java.util.regex.Pattern.compile(regex);
        return str -> p.matcher(str).find() ? Optional.ofNullable(str) : Optional.empty();
    }

    public static Pattern<Integer> inRange(Integer start, Integer end) {
        return a -> (a != null && (start == null || start < a) && (end == null || a < end)) ? Optional.of(a)
                : Optional.empty();
    }

    public static <T> SWITCH<T> Switch(T t) {
        return new SWITCH<T>(t);
    }

    public static class SWITCH<T> {
        private T val;

        private SWITCH(T obj) {
            val = obj;
        }

        @SafeVarargs
        @SuppressWarnings({ "unchecked", "varargs" })
        final public <R> Optional<R> of(CASE<? super T, ? extends R>... cases) {
            Optional<R> r = Optional.empty();
            for (CASE<? super T, ? extends R> c : cases) {
                CASE<T, R> _c = (CASE<T, R>) c;
                r = _c.match(val);
                if (r.isPresent())
                    break;
            }
            return r;
        }
    }

    // 常量函数
    private static Function<?, ?> simpleFunction(Object o) {
        if (o instanceof Function)
            return (Function<?, ?>) o;
        return constant(o);
    }

    @SuppressWarnings("unchecked")
    public static <T, R> CASE<T, R> Case(R val) {
        return new CASE<T, R>(Pattern.identity(), a -> true, (Function<T, R>) simpleFunction(val));
    }

    public static <T, R> CASE<T, R> Case(Function<? super T, ? extends R> action) {
        return new CASE<T, R>(Pattern.identity(), a -> true, action);
    }

    @SuppressWarnings("unchecked")
    public static <T, R> CASE<T, R> Case(Pattern<T> pattern, R val) {
        return new CASE<T, R>(pattern, a -> true, (Function<T, R>) simpleFunction(val));
    }

    public static <T, R> CASE<T, R> Case(Pattern<? super T> pattern, Function<? super T, ? extends R> action) {
        return new CASE<T, R>(pattern, a -> true, action);
    }

    @SuppressWarnings("unchecked")
    public static <T, R> CASE<T, R> Case(Predicate<? super T> guard, R val) {
        return new CASE<T, R>(Pattern.identity(), guard, (Function<T, R>) simpleFunction(val));
    }

    public static <T, R> CASE<T, R> Case(Predicate<? super T> guard, Function<? super T, ? extends R> action) {
        return new CASE<T, R>(Pattern.identity(), guard, action);
    }

    @SuppressWarnings("unchecked")
    public static <T, R> CASE<T, R> Case(Pattern<? super T> pattern, Predicate<? super T> guard, R val) {
        return new CASE<T, R>(pattern, guard, (Function<T, R>) simpleFunction(val));
    }

    public static <T, R> CASE<T, R> Case(Pattern<? super T> pattern, Predicate<? super T> guard,
            Function<? super T, ? extends R> action) {
        return new CASE<T, R>(pattern, guard, action);
    }

    public static class CASE<T, R> {
        // 逆变 协变
        private Pattern<T> pattern;
        private Predicate<T> guard;
        private Function<T, R> action;

        Optional<R> match(T t) {
            Optional<? extends T> opt = ((Pattern<T>) pattern).apply(t);
            return opt.isPresent() && guard.test(t) ? Optional.ofNullable(action.apply(opt.get())) : Optional.empty();
        }

        @SuppressWarnings({ "unchecked" })
        public CASE(Pattern<? super T> pattern, Predicate<? super T> guard, Function<? super T, ? extends R> action) {
            this.pattern = (Pattern<T>) pattern;
            this.guard = (Predicate<T>) guard;
            this.action = (Function<T, R>) action;
        }

    }

    public static <T> Pattern<T> pattern(Function<? super T, ? extends T> fx) {
        return a -> Optional.ofNullable(fx.apply(a));
    }

    @FunctionalInterface
    public static interface Pattern<T> extends Function<T, Optional<? extends T>> {

        static <T> Pattern<T> identity() {
            return t -> Optional.ofNullable(t);
        }
    }

    @SuppressWarnings("unchecked")
    private static <T> Optional<? extends T> unapply(T obj, Object... args) {
        Class<?> clazz = obj.getClass();
        Class<?>[] types = Arrays.stream(args).map(a -> a.getClass()).toArray(Class[]::new);
        Optional<? extends T> result = Optional.empty();
        Optional<Executable> res;
        try {
            Method[] methods = getPublicMethodsNamed(clazz, "unapply");
            res = findFirstByParameterTypes(methods, types);
            if (res.isPresent()) {
                return (Optional<? extends T>) ((Method) res.get()).invoke(obj, args);
            }
            Constructor<?>[] constructors = obj.getClass().getDeclaredConstructors();
            res = findFirstByParameterTypes(constructors, types);
            if (res.isPresent()) {
                T t = (T) (((Constructor<?>) res.get()).newInstance(args));
                if (t.equals(obj))
                    return Optional.of(t);
                return Optional.empty();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

}
